package com.franklin.maven.localcleaner.ui.tree;

import com.formdev.flatlaf.extras.*;
import com.franklin.maven.localcleaner.action.ActionUtils;
import com.franklin.maven.localcleaner.api.swing.tree.HighlightTextTreeCellRenderer;
import com.franklin.maven.localcleaner.api.swing.text.PlaceHolderTextField;
import com.franklin.maven.localcleaner.repository.LocalRepositoryFinder;
import com.franklin.maven.localcleaner.threadpool.ThreadPool;
import com.franklin.maven.localcleaner.utils.DialogUtils;
import com.franklin.maven.localcleaner.utils.DocumentUtils;
import com.franklin.maven.localcleaner.utils.ProjectUtils;
import com.intellij.icons.AllIcons;
import com.intellij.ide.DataManager;
import com.intellij.openapi.actionSystem.*;
import com.intellij.openapi.application.ApplicationManager;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.ui.popup.JBPopupFactory;
import com.intellij.openapi.ui.popup.ListPopup;
import com.intellij.openapi.util.NlsActions;
import com.intellij.openapi.util.TextRange;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.psi.codeStyle.MinusculeMatcher;
import com.intellij.psi.codeStyle.NameUtil;
import com.intellij.ui.awt.RelativePoint;
import com.intellij.uiDesigner.core.*;
import com.intellij.util.containers.FList;
import com.othelle.jtuples.Tuples;
import icons.MavenIcons;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.idea.maven.project.actions.ReimportAction;

import javax.swing.*;
import javax.swing.tree.TreePath;
import java.awt.*;
import java.awt.event.*;
import java.util.*;
import java.util.List;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.stream.Collectors;


/**
 * 本地maven仓库树结构表单
 *
 * @author: Ye Junhui
 * @since : 2022/4/2 0002
 */
public class LocalMavenTreeForm {

    private List<String> localJarNames = Collections.emptyList();

    private int lastInputLength = 0;

    public LocalMavenTreeForm() {
        initComponents();
        this.init();
    }

    /**
     * 初始化
     */
    private void init() {
        this.initLocalJarNames();
        this.initRefreshBtn();
        this.initRefreshMavenBtn();
        this.initSearchTextField();
        this.initTree();
    }

    private void initLocalJarNames() {
        //加载maven jar包信息
        Project project = ProjectUtils.getCurrProject();
        List<VirtualFile> localPackages = LocalRepositoryFinder.findLocalPackages(project);
        VirtualFile localRepositoryPath = LocalRepositoryFinder.findLocalRepositoryPath(project);
        if (Objects.isNull(localRepositoryPath)) {
            this.localJarNames = Collections.emptyList();
            return;
        }
        String repPath = localRepositoryPath.getPath();
        this.localJarNames = localPackages.stream()
                .map(localPackage -> {
                    String packagePath = localPackage.getPath();
                    String[] split = packagePath.replaceAll(repPath, "").substring(1).split("/");
                    StringBuilder builder = new StringBuilder();
                    String[] strings = Arrays.copyOf(split, split.length - 1);
                    String join = String.join(".", strings);
                    builder.append(join).append(":").append(split[split.length - 1]);
                    return builder.toString();
                })
                .collect(Collectors.toList());
    }

    /**
     * 初始化搜索框
     */
    private void initSearchTextField() {
        this.searchTextField.setPlaceHolder("Search For Jars");
        this.searchTextField.addCaretListener(e -> {
            int length = this.searchTextField.getInputText().length();
            if (length != this.lastInputLength) {
                if (length < 2) {
                    if (length == 0) {
                        LocalMavenTreeForm.this.initTree();
                    }
                } else {
                    LocalMavenTreeForm.this.initSearchTree();
                }
                this.lastInputLength = length;
            }
        });
    }

    /**
     * 初始化搜索结果
     */
    private void initSearchTree() {
        ThreadPool.getExecutorService().execute(() -> {
            String input = this.searchTextField.getInputText();
            MinusculeMatcher matcher = NameUtil.buildMatcher("*" + input, NameUtil.MatchingCaseSensitivity.NONE);
            MavenNode mavenNode = new MavenNode(null);
            List<MavenJarNode> mavenJarNodeList = this.localJarNames.stream()
                    .filter(matcher::matches)
                    .map(jarName -> {
                        FList<TextRange> textRanges = matcher.matchingFragments(jarName);
                        List<String> highlightTextList = textRanges.stream()
                                .map(textRange -> jarName.substring(textRange.getStartOffset(), textRange.getEndOffset()))
                                .collect(Collectors.toList());
                        return Tuples.tuple(matcher.matchingDegree(jarName), jarName, highlightTextList);
                    })
                    .sorted(Comparator.comparingInt(t -> -t._1()))
                    .map(t -> new MavenJarNode(mavenNode, t._2(), t._3()))
                    .collect(Collectors.toList());

            MavenTreeModel mavenTreeModel = new MavenTreeModel(mavenNode, mavenJarNodeList);
            this.localMavenTree.setModel(mavenTreeModel);
            this.initTreeFunction(this.localMavenTree);
            this.localMavenTree.setVisible(true);
        });
    }

    /**
     * 初始化刷新maven按钮
     */
    private void initRefreshMavenBtn() {
        this.refreshMavenBtn.setIcon(MavenIcons.MavenLoadChanges);
        this.refreshMavenBtn.addActionListener(e -> {
            ThreadPool.getExecutorService().execute(() -> {
                ActionUtils.refreshMavenProjects(ProjectUtils.getCurrProject(), null);
                this.refreshBtn.doClick();
            });
        });
    }

    /**
     * 初始化刷新按钮
     */
    private void initRefreshBtn() {
        this.refreshBtn.addActionListener(e -> {
            ThreadPool.getExecutorService().execute(() -> {
                this.initLocalJarNames();
                this.initTree();
                this.searchTextField.refresh();
            });
        });
    }

    /**
     * 初始化树
     */
    private void initTree() {
        ThreadPool.getExecutorService().execute(() -> {
            MavenNode mavenNode = new MavenNode(null);
            List<MavenJarNode> mavenJarNodeList = this.localJarNames.stream()
                    .map(jarName -> new MavenJarNode(mavenNode, jarName))
                    .collect(Collectors.toList());
            MavenTreeModel mavenTreeModel = new MavenTreeModel(mavenNode, mavenJarNodeList);
            this.localMavenTree.setModel(mavenTreeModel);
            this.initTreeFunction(this.localMavenTree);
            this.localMavenTree.setVisible(true);
        });
    }

    public JComponent getMainPanel() {
        return this.mainPanel;
    }

    private void initTreeFunction(JTree jTree) {
        HighlightTextTreeCellRenderer highLightTextTreeCellRenderer = new HighlightTextTreeCellRenderer(Color.BLACK);
        jTree.setCellRenderer(highLightTextTreeCellRenderer);
        highLightTextTreeCellRenderer.setLeafIcon(AllIcons.Nodes.PpLib);
        highLightTextTreeCellRenderer.setOpenIcon(MavenIcons.ToolWindowMaven);
        highLightTextTreeCellRenderer.setClosedIcon(MavenIcons.ToolWindowMaven);

        jTree.addMouseListener(new MouseListener() {
            @Override
            public void mouseClicked(MouseEvent e) {
                int button = e.getButton();
                TreePath pathForLocation = jTree.getPathForLocation(e.getX(), e.getY());
                if (Objects.isNull(pathForLocation)) {
                    return;
                }
                if (button == 3) {
                    JBPopupFactory jbPopupFactory = JBPopupFactory.getInstance();

                    ListPopup actionGroupPopup = jbPopupFactory.createActionGroupPopup(
                            null,
                            new JarNodeActionGroup(pathForLocation, e),
                            DataManager.getInstance().getDataContext(localMavenTree),
                            JBPopupFactory.ActionSelectionAid.NUMBERING,
                            false
                    );
                    actionGroupPopup.show(new RelativePoint(e));

                }
            }

            @Override
            public void mousePressed(MouseEvent e) {

            }

            @Override
            public void mouseReleased(MouseEvent e) {

            }

            @Override
            public void mouseEntered(MouseEvent e) {

            }

            @Override
            public void mouseExited(MouseEvent e) {

            }
        });
    }

    /**
     * 移除本地Jar包Action
     */
    private abstract class BaseRemoveJarAction extends AnAction {

        protected final TreePath selectPath;
        protected final MouseEvent mouseEvent;

        public BaseRemoveJarAction(@Nullable @NlsActions.ActionText String text,
                                   @Nullable @NlsActions.ActionDescription String description,
                                   TreePath selectPath,
                                   MouseEvent mouseEvent) {
            super(text, description, AllIcons.General.Remove);
            this.selectPath = selectPath;
            this.mouseEvent = mouseEvent;
        }

        @Override
        public void actionPerformed(@NotNull AnActionEvent e) {
            if (DialogUtils.showConfirmDialog("Confirm Remove This Jar", "Are you sure to remove this package?")) {
                doActionPerformed(e);
            }
        }

        protected boolean doActionPerformed(@NotNull AnActionEvent e) {
            MavenJarNode mavenJarNode = (MavenJarNode) selectPath.getLastPathComponent();
            AtomicBoolean result = new AtomicBoolean(false);
            DocumentUtils.writeSync(() -> {
                try {
                    LocalRepositoryFinder.removeJar(mavenJarNode.getNodeProject(), this.getJarPath());
                    LocalMavenTreeForm.this.refreshBtn.doClick();
                    result.set(true);
                } catch (Exception ioException) {
                    ioException.printStackTrace();
                    DialogUtils.showConfirmDialog("IO Exception", "它可能正在被使用请检查");
                    result.set(false);
                }
            });
            return result.get();
        }

        /**
         * 将jar包名转换jar包路径
         *
         * @return
         */
        protected String getJarPath() {
            MavenJarNode mavenJarNode = (MavenJarNode) selectPath.getLastPathComponent();
            String name = mavenJarNode.getName();
            String[] split = name.split(":");
            String packageStr = split[0].replaceAll("\\.", "/");
            return packageStr + "/" + split[1];
        }
    }

    /**
     * 移除本地jar包
     */
    private class RemoveJarAction extends BaseRemoveJarAction {

        public RemoveJarAction(TreePath selectPath, MouseEvent mouseEvent) {
            super("Remove This Jar",
                    "Remove this package from local repository",
                    selectPath,
                    mouseEvent);
        }
    }

    /**
     * 移除本地jar包并刷新maven
     * see also: {@link ReimportAction}
     */
    private class RemoveJarAndRefreshMavenAction extends BaseRemoveJarAction {

        public RemoveJarAndRefreshMavenAction(TreePath selectPath, MouseEvent mouseEvent) {
            super("Remove This Jar And Refresh Maven",
                    "Remove this package from local repository and refresh maven package of this project",
                    selectPath,
                    mouseEvent);
        }

        @Override
        protected boolean doActionPerformed(@NotNull AnActionEvent e) {
            MavenJarNode mavenJarNode = (MavenJarNode) selectPath.getLastPathComponent();
            if (super.doActionPerformed(e)) {
                //执行成功并刷新
                return ActionUtils.refreshMavenProjects(mavenJarNode.getNodeProject(), e);
            }
            return false;
        }
    }

    /**
     * 打开jar目录Action
     */
    private class OpenJarPathInExplorerAction extends BaseRemoveJarAction {

        public OpenJarPathInExplorerAction(TreePath selectPath, MouseEvent mouseEvent) {
            super("Open In Explorer", "Open this package folder in explorer", selectPath, mouseEvent);
            this.getTemplatePresentation().setIcon(AllIcons.General.OpenDisk);
        }

        @Override
        public void actionPerformed(@NotNull AnActionEvent e) {
            MavenJarNode mavenJarNode = (MavenJarNode) selectPath.getLastPathComponent();
            VirtualFile localRepositoryPath = LocalRepositoryFinder.findLocalRepositoryPath(mavenJarNode.getNodeProject());
            if (Objects.isNull(localRepositoryPath)) {
                return;
            }
            ActionUtils.openFileInExplorer(localRepositoryPath.getPath() + "/" + this.getJarPath());
        }
    }

    /**
     * Jar包Action
     */
    private class JarNodeActionGroup extends DefaultActionGroup {

        protected final TreePath selectPath;
        protected final MouseEvent mouseEvent;

        public JarNodeActionGroup(TreePath selectPath, MouseEvent mouseEvent) {
            this.selectPath = selectPath;
            this.mouseEvent = mouseEvent;

            this.add(new RemoveJarAction(this.selectPath, this.mouseEvent));
            this.add(new RemoveJarAndRefreshMavenAction(this.selectPath, this.mouseEvent));
            this.add(new OpenJarPathInExplorerAction(this.selectPath, this.mouseEvent));
        }
    }

    private void initComponents() {
        // JFormDesigner - Component initialization - DO NOT MODIFY  //GEN-BEGIN:initComponents  @formatter:off
        mainPanel = new JPanel();
        treeScrollPane = new JScrollPane();
        localMavenTree = new JTree();
        topPanel = new JPanel();
        refreshBtn = new JButton();
        refreshMavenBtn = new JButton();
        searchTextField = new PlaceHolderTextField();

        //======== mainPanel ========
        {
            mainPanel.setLayout(new GridLayoutManager(2, 1, new Insets(0, 0, 0, 0), -1, -1));

            //======== treeScrollPane ========
            {
                treeScrollPane.setViewportView(localMavenTree);
            }
            mainPanel.add(treeScrollPane, new GridConstraints(1, 0, 1, 1,
                GridConstraints.ANCHOR_CENTER, GridConstraints.FILL_BOTH,
                GridConstraints.SIZEPOLICY_CAN_SHRINK | GridConstraints.SIZEPOLICY_CAN_GROW | GridConstraints.SIZEPOLICY_WANT_GROW,
                GridConstraints.SIZEPOLICY_CAN_SHRINK | GridConstraints.SIZEPOLICY_CAN_GROW | GridConstraints.SIZEPOLICY_WANT_GROW,
                null, null, null));

            //======== topPanel ========
            {
                topPanel.setLayout(new GridLayoutManager(1, 3, new Insets(0, 0, 0, 0), -1, -1));

                //---- refreshBtn ----
                refreshBtn.setIcon(new FlatSVGIcon("actions/refresh_dark.svg"));
                refreshBtn.setText("");
                topPanel.add(refreshBtn, new GridConstraints(0, 0, 1, 1,
                    GridConstraints.ANCHOR_CENTER, GridConstraints.FILL_HORIZONTAL,
                    GridConstraints.SIZEPOLICY_CAN_SHRINK | GridConstraints.SIZEPOLICY_CAN_GROW,
                    GridConstraints.SIZEPOLICY_FIXED,
                    null, null, null));

                //---- refreshMavenBtn ----
                refreshMavenBtn.setText("");
                topPanel.add(refreshMavenBtn, new GridConstraints(0, 1, 1, 1,
                    GridConstraints.ANCHOR_CENTER, GridConstraints.FILL_HORIZONTAL,
                    GridConstraints.SIZEPOLICY_CAN_SHRINK | GridConstraints.SIZEPOLICY_CAN_GROW,
                    GridConstraints.SIZEPOLICY_FIXED,
                    null, null, null));
                topPanel.add(searchTextField, new GridConstraints(0, 2, 1, 1,
                    GridConstraints.ANCHOR_WEST, GridConstraints.FILL_HORIZONTAL,
                    GridConstraints.SIZEPOLICY_CAN_GROW | GridConstraints.SIZEPOLICY_WANT_GROW,
                    GridConstraints.SIZEPOLICY_FIXED,
                    null, null, null));
            }
            mainPanel.add(topPanel, new GridConstraints(0, 0, 1, 1,
                GridConstraints.ANCHOR_CENTER, GridConstraints.FILL_BOTH,
                GridConstraints.SIZEPOLICY_CAN_SHRINK | GridConstraints.SIZEPOLICY_CAN_GROW,
                GridConstraints.SIZEPOLICY_CAN_SHRINK | GridConstraints.SIZEPOLICY_CAN_GROW,
                null, null, null));
        }
        // JFormDesigner - End of component initialization  //GEN-END:initComponents  @formatter:on
    }

    // JFormDesigner - Variables declaration - DO NOT MODIFY  //GEN-BEGIN:variables  @formatter:off
    private JPanel mainPanel;
    private JScrollPane treeScrollPane;
    private JTree localMavenTree;
    private JPanel topPanel;
    private JButton refreshBtn;
    private JButton refreshMavenBtn;
    private PlaceHolderTextField searchTextField;
    // JFormDesigner - End of variables declaration  //GEN-END:variables  @formatter:on
}
